import type { FetchOptions } from 'ofetch'
import { cryptoUtils } from '~/utils/crypto/core'
import { decryptData, encryptData } from '~/utils/crypto'
import { CODE_INVALID_AUTH, CODE_SYSTEM_MAINTENANCE } from '~/enums/code'
import Cache from '~/utils/cache'
import { MessageEle } from '~/plugins/message.client'

type NitroFetchOptions = Parameters<typeof $fetch>[1]

type HttpOptions = NitroFetchOptions & {
  url: string
}

export interface HttpResponse<T = unknown> {
  code: number
  msg: string
  data: T
}

class HttpRequest {
  private appKey: string = import.meta.env.VITE_APP_KEY

  // private blobParseType: string = 'application/json'

  request<T = unknown>(options: HttpOptions) {
    // let baseApi: string = import.meta.env.VITE_APP_BASE_API
    // if (process.server) {
    //   baseApi.includes('api') && runtimeConfig.serverBaseApi[import.meta.env.VITE_APP_ENV_FLAG]
    // }
    const baseApi = process.client
      ? import.meta.env.VITE_APP_BASE_API
      : useRuntimeConfig().serverBaseApi[import.meta.env.VITE_APP_ENV_FLAG]

    const defaultOptions: NitroFetchOptions = {
      baseURL: baseApi,
      headers: {
        'X-Request-Token': Cache.token() as string,
        'Content-Type': 'application/json',
        'ob-client': import.meta.env.VITE_APP_CLIENT,
        'ob-application': import.meta.env.VITE_APP_APPLICATION,
        'ob-secret-version': import.meta.env.VITE_APP_SECRET_VERSION,
        'Merchant-Id': import.meta.env.VITE_APP_MERCHANT_ID
      },
      onRequest: ({ options }) => {
        options.headers = new Headers(options.headers)
        if (this.cryptoCheck()) {
          if (['dev', 'test'].includes(import.meta.env.VITE_APP_ENV_FLAG)) {
            options.headers.set('ob-encrypted', 'true')
          }
          this.encryptReqData(options)
        } else {
          options.headers.set('ob-encrypted', 'false')
          options.headers.set('ob-nonce', cryptoUtils.createNonce().toString())
          options.headers.set('ob-timestamp', cryptoUtils.createTimestamp().toString())
        }
      },
      onResponse: async ({ response, options }) => {
        if (response) {
          response._data = this.decryptResData(response._data)
          if (response._data.code !== 200) {
            if (process.server) {
              console.log('')
              console.log('接口==>:', options.url)
              console.log('请求==>：', options)
              console.log('响应==>：', response._data)
              console.log('')
            }

            if (CODE_INVALID_AUTH.includes(response._data.code) && options.url !== '/video/user/auto/register') {
              // 处理登出
              await useUser().handleLogout(2)
            }
            return Promise.reject(response._data)
          }
        }
      },
      onResponseError: async ({ response }) => {
        if (response) {
          if (response._data) {
            response._data = this.decryptResData(response._data)
          }
          // 停服提示
          if (response._data?.code === CODE_SYSTEM_MAINTENANCE) {
            // 处理登出
          }
        }
      }
    }
    return new Promise<HttpResponse<T>>((resolve, reject) => {
      $fetch(options.url, useMerge(options, defaultOptions))
        .then((data) => {
          resolve(data as HttpResponse<T>)
        })
        .catch((error) => {
          let msg = error.msg || error.message || ''
          if (msg.includes('Failed to fetch') || msg.includes('NetworkError')) {
            msg = '网络异常，请检查网络'
          } else if (msg.includes('aborted')) {
            msg = '您的服务器走丢了，请稍等几分钟再试'
          }
          MessageEle.error(msg || '系统异常，请稍后重试')
          reject(error)
        })
    })
  }

  get<T = unknown>(options: HttpOptions) {
    return this.request<T>({
      ...options,
      method: 'GET'
    })
  }

  post<T = unknown>(options: HttpOptions) {
    return this.request<T>({
      ...options,
      method: 'POST'
    })
  }

  private cryptoCheck() {
    // return true
    return import.meta.env.PROD && import.meta.env.VITE_APP_ENCRYPTED === '1' && process.client
  }

  private decryptResData(data: any) {
    if (data && this.cryptoCheck()) {
      data = JSON.parse(decryptData(data) || '')
    }
    return data
  }

  private encryptReqData(options: FetchOptions) {
    const encryptedData = encryptData(options.body)
    const nonce = cryptoUtils.createNonce().toString()
    const timestamp = cryptoUtils.createTimestamp().toString()
    options.body = encryptedData
    options.headers = new Headers(options.headers)
    options.headers.set('ob-sign', cryptoUtils.createSign(encryptedData, nonce, timestamp, this.appKey))
    options.headers.set('ob-nonce', nonce)
    options.headers.set('ob-timestamp', timestamp)
    return options
  }
}

export default new HttpRequest()
